package com.tianji.learning.utils;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.utils.JsonUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

/**
 * @param
 * @return
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class HandeLearningRecordCache {
    private final StringRedisTemplate redisTemplate;
    private DelayQueue<DelayTask<DelayRecordData>> delayQueue = new DelayQueue<>();
    private final String KEY_TEMPLATE = "learning:record:{}";
    private Boolean flag = true;

    private final LearningRecordMapper recordMapper;
    private final LearningLessonMapper lessonMapper;

    /**
     * 随着项目启动开启一条副线程执行该任务
     */
    @PostConstruct
    public void init() {
        /*new Thread(new Runnable() {
            @Override
            public void run() {
                delayTaskAndredisRecordCompareto();
            }
        }).start();*/

        CompletableFuture.runAsync(() -> delayTaskAndredisRecordCompareto());
    }

    @PreDestroy
    public void destory() {
        log.info("延迟任务开始销毁。。。");
        flag = false;
    }


    /**
     * 延迟任务数据与redis中的数据比较
     */
    public void delayTaskAndredisRecordCompareto() {
        log.info("延迟任务执行了。。。。");
        while (flag) {
            try {
                //take延迟任务拿到数据
                DelayTask<DelayRecordData> take = delayQueue.take();
                DelayRecordData delayData = take.getData();
                log.info("获取到延时监测任务");
                //拿到redis中的数据
                LearningRecord learningRecord = queryRedisData(delayData.getLeassonId(), delayData.getSectionId());
                //进行比较
                if (learningRecord == null || !Objects.equals(delayData.getMoment(), learningRecord.getMoment())) {
                    //如果不一致，说明用户还在看，把无需处理
                    continue;
                }
                //不一致说明已经离开
                //更新学习记录表中的数据
                learningRecord.setFinished(null);
                recordMapper.updateById(learningRecord);
                //更新课表的数据
                LearningLesson learningLesson = new LearningLesson();
                learningLesson.setId(delayData.getLeassonId());
                learningLesson.setLatestSectionId(delayData.getSectionId());
                learningLesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                lessonMapper.updateById(learningLesson);
            } catch (InterruptedException e) {
                log.error("执行播放进度延迟任务失败{}", e);
            }


        }
    }


    /**
     * 查询redis中的学习记录
     *
     * @param lessonId
     * @param sectionId
     * @return
     */
    public LearningRecord queryRedisData(Long lessonId, Long sectionId) {
        log.info("开始从redis中读取数据，课表id{}，小节id{}", lessonId, sectionId);
        //组装大Key
        String Key = StringUtils.format(KEY_TEMPLATE, lessonId);

        //小key
        String hashKey = sectionId.toString();
        //查询学习记录
        String hashValue = (String) redisTemplate.opsForHash().get(Key, hashKey);
        if (ObjectUtil.isEmpty(hashValue)) {
            //查询结果为空
            log.error("查询的学习记录为空,课表为{}，小节id为{}", lessonId, sectionId);
            return null;
        }
        //不为空将数据拷贝为LearningRecod
        LearningRecord learningRecord = JsonUtils.toBean(hashValue, LearningRecord.class);
        return learningRecord;
    }

    /**
     * 将学习记录写入redis
     *
     * @param learningRecord
     */
    public void writeRedisLearningRecord(LearningRecord learningRecord) {
        //组装大key
        String Key = StringUtils.format(KEY_TEMPLATE, learningRecord.getLessonId());
        //组装小key
        String hashKey = learningRecord.getSectionId().toString();
        //组装hashValue数据

        String hashValue = JsonUtils.toJsonStr(new LearningRecodData(learningRecord));
        //将学习记录写入redis
        redisTemplate.opsForHash().put(Key, hashKey, hashValue);
        //设置redis数据的过期时间
        redisTemplate.expire(Key, Duration.ofMinutes(1));
    }

    /**
     * 更新redis中的播放进度和提交延时任务
     *
     * @param learningRecord
     */
    public void updateRedisDateAndDelayCommit(LearningRecord learningRecord) {
        log.info("向redis中添加学习进度覆盖之前的学习进度{}", learningRecord);
        //先覆盖redis之前的数据
        writeRedisLearningRecord(learningRecord);
        //添加延时任务，20秒后执行
        delayQueue.add(new DelayTask<>(Duration.ofSeconds(20), new DelayRecordData(learningRecord)));
    }

    /**
     * 删除redis中的学习记录数据
     *
     * @param lessonId
     * @param sectionId
     */
    public void removeRedisLearningRecord(Long lessonId, Long sectionId) {
        log.info("清除redis中的数据，课表id{},小节id{}", lessonId, sectionId);
        //组装大Key
        String Key = StringUtils.format(KEY_TEMPLATE, lessonId);
        //组装小key（hashKey）
        String hashKey = sectionId.toString();
        //删除
        redisTemplate.opsForHash().delete(Key, hashKey);
    }

    @Data
    class LearningRecodData {
        private Long id;
        private Integer moment;
        private Boolean finished;

        public LearningRecodData(LearningRecord learningRecord) {
            this.id = learningRecord.getId();
            this.moment = learningRecord.getMoment();
            this.finished = learningRecord.getFinished();

        }//DelayRecordData
    }

    @Data
    class DelayRecordData {
        private Long leassonId;
        private Long sectionId;
        private Integer moment;

        public DelayRecordData(LearningRecord learningRecord) {
            this.leassonId = learningRecord.getLessonId();
            this.sectionId = learningRecord.getSectionId();
            this.moment = learningRecord.getMoment();

        }//DelayRecordData
    }
}
